#region Using directives
using System;
using SharpWind.Helpers;
using SharpWind.Graphics;
using System.Text;
using System.Drawing;
using Lidgren.Library.Network;
using SharpWind.Network;
using SharpWind.Resources.Sound;
#endregion

namespace SharpWind.Performance
{
	public class PerformanceMonitor
	{
		#region Fields

		private long totalFrames;

		private long currentFrames;

		private long fpsUpdateInterval;

		private long lastFpsUpdate;

		private float currentFps;

		private float maxFps;

		private float minFps;

		private float averageFps;

		private float lastFrameTime = 0;

		private float averageFrameTime = 0;

		private float bestFrameTime = float.MaxValue;

		private float worstFrameTime = float.MinValue;

		private Timer timer;

		private SharpWind.Graphics.Font font;

		#endregion

		#region Constructor

		public PerformanceMonitor( Timer timer )
		{
			if( timer == null )
			{
				throw new ArgumentNullException( "timer" );
			}

			this.timer = timer;

			this.fpsUpdateInterval = timer.TicksPerSecond >> 1;
			this.maxFps = long.MinValue;
			this.minFps = long.MaxValue;

			this.font = new SharpWind.Graphics.Font( "Courier", 10, System.Drawing.FontStyle.Regular );
		}

		#endregion

		#region Methods

		internal void Update()
		{
			#region Fps Calculations

			this.totalFrames++;
			this.currentFrames++;

			long ct = this.timer.CurrentTime;

			if( ( ct - this.lastFpsUpdate ) >= this.fpsUpdateInterval )
			{
				float time		= (float)ct / (float)this.timer.TicksPerSecond;
				float lastTime  = this.lastFpsUpdate / (float)this.timer.TicksPerSecond;

				this.currentFps = (float)this.currentFrames / ( time - lastTime );

				if( this.currentFps > this.maxFps )
				{
					this.maxFps = this.currentFps;
				}
				else if( this.currentFps < this.minFps )
				{
					this.minFps = this.currentFps;
				}

				this.averageFps = this.totalFrames / this.timer.RunningTime;

				this.currentFrames = 0;
				this.lastFpsUpdate = ct;
			}

			#endregion

			#region Frame time Calculations

			this.lastFrameTime = this.timer.ElapsedTime;

			this.averageFrameTime = ( this.timer.RunningTime ) / this.totalFrames;

			if( this.lastFrameTime < this.bestFrameTime && this.lastFrameTime != 0 )
			{
				this.bestFrameTime = this.lastFrameTime;
			}

			if( this.lastFrameTime > this.worstFrameTime )
			{
				this.worstFrameTime = this.lastFrameTime;
			}

			#endregion
		}

		internal void Draw()
		{
			StringBuilder sb = new StringBuilder();
			sb.AppendLine( "FPS Statistics (Higher is better)" );
			sb.AppendLine( string.Format( "  Current     : {0}", this.currentFps ) );
			sb.AppendLine( string.Format( "  Average     : {0}", this.averageFps ) );
			sb.AppendLine( string.Format( "  Best        : {0}", this.maxFps ) );
			sb.AppendLine( string.Format( "  Worst       : {0}", this.minFps ) );
			sb.AppendLine( "Frame Time Statistics (Lower is better)" );
			sb.AppendLine( string.Format( "  Last        : {0}", this.lastFrameTime ) );
			sb.AppendLine( string.Format( "  Average     : {0}", this.averageFrameTime ) );
			sb.AppendLine( string.Format( "  Best        : {0}", this.bestFrameTime ) );
			sb.AppendLine( string.Format( "  Worst       : {0}", this.worstFrameTime ) );
			sb.AppendLine( "Sound Statistics" );

			SoundManager sound = Engine.Instance.Sound;
			sb.AppendLine( string.Format( "  Max Sources : {0}", sound.MaxSources ) );
			sb.AppendLine( string.Format( "  Total Sounds: {0}", sound.TotalSounds ) );
			sb.AppendLine( string.Format( "  Memory Usage: {0}", sound.MemoryUsage ) );
			
			sb.AppendLine( "Network Statistics" );
			sb.AppendLine( string.Format( "  Status      : {0}", Engine.Instance.Network.Status.ToString() ) );
			if( !string.IsNullOrEmpty( Engine.Instance.Network.CurrentReason ) )
			{
				sb.AppendLine( string.Format( "  Reason      : {0}", Engine.Instance.Network.CurrentReason ) );
			}
			if( Engine.Instance.Network.Status == NetConnectionStatus.Connected )
			{
				Connection serverConnection = Engine.Instance.Network.ServerConnection;

				sb.AppendLine( string.Format( "  Connected to: {0}", serverConnection.EndPoint.ToString() ) );
				sb.AppendLine( string.Format( "  Average Ping: {0}", serverConnection.AverageRoundtripTime ) );
				sb.AppendLine( "  Packets" );
				sb.AppendLine( string.Format( "    Sent      : {0}", serverConnection.Statistics.PacketsSent ) ); 
				sb.AppendLine( string.Format( "    Received  : {0}", serverConnection.Statistics.PacketsReceived ) );
				sb.AppendLine( "  Messages" );
				sb.AppendLine( string.Format( "    Sent      : {0}", serverConnection.Statistics.MessagesSent ) );
				sb.AppendLine( string.Format( "    Resent    : {0}", serverConnection.Statistics.MessagesResent ) );
				sb.AppendLine( string.Format( "    Received  : {0}", serverConnection.Statistics.MessagesReceived ) );
				sb.AppendLine( "  User Messages" );
				sb.AppendLine( string.Format( "    Sent      : {0}", serverConnection.Statistics.UserMessagesSent ) );
				sb.AppendLine( string.Format( "    Received  : {0}", serverConnection.Statistics.UserMessagesReceived ) );
				sb.AppendLine( "  Ack Messages"  );
				sb.AppendLine( string.Format( "    Sent      : {0}", serverConnection.Statistics.AckMessagesSent ) );
				sb.AppendLine( string.Format( "    Received  : {0}", serverConnection.Statistics.AckMessagesReceived ) );
				sb.AppendLine( "  Bytes" );
				sb.AppendLine( string.Format( "    Sent      : {0}", serverConnection.Statistics.BytesSent ) );
				sb.AppendLine( string.Format( "    Received  : {0}", serverConnection.Statistics.BytesReceived ) );
			}

			sb.AppendLine();
			sb.AppendLine( string.Format( "Total Frames  : {0}", this.totalFrames ) );
			sb.AppendLine( string.Format( "Running Time  : {0}", this.timer.RunningTime ) );

			font.DrawText( sb.ToString(), 10, 10, Color.White );
		}

		#endregion

		#region Properties

		public float CurrentFps
		{
			get
			{
				return this.currentFps;
			}
		}

		public float MaxFps
		{
			get
			{
				return this.maxFps;
			}
		}

		public float MinFps
		{
			get
			{
				return this.minFps;
			}
		}

		public float AverageFps
		{
			get
			{
				return (float)this.totalFrames / this.timer.RunningTime;
			}
		}

		#endregion
	}
}